<?php
// +----------------------------------------------------------------------
// | ThinkCMF [ WE CAN DO IT MORE SIMPLE ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013-2018 http://www.thinkcmf.com All rights reserved.
// +----------------------------------------------------------------------
// | Author: 五五 <15093565100@163.com>
// +----------------------------------------------------------------------
namespace plugins\oss\controller;

use cmf\controller\PluginBaseController;
use think\facade\Log;
use think\Validate;
use plugins\oss\lib\Oss;
use think\facade\Db;
use DateTime;

class AssetController extends PluginBaseController
{
    public function getUrl()
    {

        $oss = new Oss([]);

        $fileHash = $this->request->param('file_hash');
        $filname  = $this->request->param('filename');
        $fileType = $this->request->param('filetype');

        $suffix = cmf_get_file_extension($filname);

        $file = $fileHash.".{$suffix}";

        $previewUrl = $fileType == 'image' ? $oss->getPreviewUrl($file) : $oss->getFileDownloadUrl($file);
        $url        = $fileType == 'image' ? $oss->getImageUrl($file, 'watermark') : $oss->getFileDownloadUrl($file);

        return $this->success('success', null, [
            'url'         => $url,
            'preview_url' => $previewUrl,
            'filepath'    => $file
        ]);
    }

    protected function gmt_iso8601($time)
    {
        $dtStr      = date("c", $time);
        $mydatetime = new DateTime($dtStr);
        $expiration = $mydatetime->format(DateTime::ISO8601);
        $pos        = strpos($expiration, '+');
        $expiration = substr($expiration, 0, $pos);
        return $expiration."Z";
    }

    public function getConfig()
    {
        $app = $this->request->param('app','');
        $filename = $this->request->param('filename','');
        $userId = cmf_get_current_admin_id();
        $userId = $userId ? $userId : cmf_get_current_user_id();
        if (empty($userId)) {
            $this->error('error');
        }
        $pluginClass = cmf_get_plugin_class('Oss');

        $oss         = new $pluginClass();
        $ossConfig   = $oss->getConfig();
        $id          = $ossConfig['accessKeyId'];
        $key         = $ossConfig['accessKeySecret'];
        $host        = $this->request->scheme().'://'.$ossConfig['bucket'].'.'.$ossConfig['endpoint'].'/';
        $callbackUrl = cmf_plugin_url("Oss://Asset/callback", [], true);

        $callback_param  = array(
            'callbackUrl'      => $callbackUrl,
            'callbackBody'     => 'etag=${etag}&filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}'.'&uid='.$userId.'&localname='.$filename,
            'callbackBodyType' => "application/x-www-form-urlencoded"
        );
        Log::info(var_export($callback_param,true));
        $callback_string = json_encode($callback_param);

        $base64_callback_body = base64_encode($callback_string);
        $now                  = time();
        $expire               = 30; //设置该policy超时时间是10s. 即这个policy过了这个有效时间，将不能访问
        $end                  = $now + $expire;
        $expiration           = $this->gmt_iso8601($end);
        $dir = $app?$app.'/':'';
        $dir .= 'file-direct-oss/'.date('Ymd').'/';
        Log::info('dir: '.$dir);
        //最大文件大小.用户可以自己设置
        $condition    = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
        $conditions[] = $condition;

        //表示用户上传的数据,必须是以$dir开始, 不然上传会失败,这一步不是必须项,只是为了安全起见,防止用户通过policy上传到别人的目录
        $start        = array(0 => 'starts-with', 1 => '$key', 2 =>$dir);
        $conditions[] = $start;


        $arr = array('expiration' => $expiration, 'conditions' => $conditions);
        //echo json_encode($arr);
        //return;
        $policy         = json_encode($arr);
        $base64_policy  = base64_encode($policy);
        $string_to_sign = $base64_policy;
        $signature      = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true));
        Log::info('$signature : '.$signature);
        $response              = array();
        $response['accessid']  = $id;
        $response['host']      = $host;
        $response['policy']    = $base64_policy;
        $response['signature'] = $signature;
        $response['expire']    = $end;
        $response['callback']  = $base64_callback_body;
        //这个参数是设置用户上传指定的前缀
        $response['dir'] = $dir;
        return json($response);
    }

    public function callback()
    {
        // 1.获取OSS的签名header和公钥url header
        $authorizationBase64 = "";
        $pubKeyUrlBase64     = "";
        if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
            $authorizationBase64 = $_SERVER['HTTP_AUTHORIZATION'];
        }
        if (isset($_SERVER['HTTP_X_OSS_PUB_KEY_URL'])) {
            $pubKeyUrlBase64 = $_SERVER['HTTP_X_OSS_PUB_KEY_URL'];
        }

        if ($authorizationBase64 == '' || $pubKeyUrlBase64 == '') {
            Log::record('callback: line '.__LINE__.' error: 签名信息不存在', 'oss');
            header("http/1.1 404 Forbidden");

            exit();
        }

        // 2.获取OSS的签名
        $authorization = base64_decode($authorizationBase64);

        // 3.获取公钥
        $pubKeyUrl = base64_decode($pubKeyUrlBase64);
        $ch        = curl_init();
        curl_setopt($ch, CURLOPT_URL, $pubKeyUrl);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
        $pubKey = curl_exec($ch);
        if ($pubKey == "") {
            Log::record('callback: line '.__LINE__.' error: 公钥请求失败', 'oss');
            header("http/1.1 403 Forbidden");
            exit();
        }

        // 4.获取回调body:
        $body = file_get_contents('php://input');
        $data = $this->request->param();
        // 5.拼接待签名字符串
        $authStr = '';
        $path    = $_SERVER['REQUEST_URI'];
        $pos     = strpos($path, '?');
        if ($pos === false) {
            $authStr = urldecode($path)."\n".$body;
        } else {
            $authStr = urldecode(substr($path, 0, $pos)).substr($path, $pos, strlen($path) - $pos)."\n".$body;
        }

        // 6.验证签名
        $ok = openssl_verify($authStr, $authorization, $pubKey, OPENSSL_ALGO_MD5);
        if ($ok == 1) {
            Log::info('callback Data: '.var_export($data,true));
            $findAsset = Db::name('asset')->where('file_key', $data['etag'])->find();
            if (empty($findAsset)) {
                $arr      = explode('/', $data['filename']);
                $filename = $data['localname']?$data['localname']:array_pop($arr);
                $suffix   = cmf_get_file_extension($filename);
                Db::name('asset')->insert([
                    'user_id'     => $data['uid'],
                    'file_size'   => $data['size'],
                    'filename'    => $data['localname'],
                    'create_time' => time(),
                    'file_key'    => $data['etag'],
                    'file_path'   => $data['filename'],
                    'suffix'      => $suffix
                ]);
                $findAsset = Db::name('asset')->where('file_key', $data['etag'])->find();
            } else {
                $suffix   = cmf_get_file_extension($findAsset['file_path']);
                $arr              = explode('/', $findAsset['file_path']);
                $filename         = $findAsset['filename']?$findAsset['filename']:array_pop($arr);
            }
            $new_data = [
                'image_url' => in_array($suffix,['jpg','jpeg','gif','png','bmp'])?cmf_get_image_preview_url($findAsset['file_path']):cmf_get_asset_url($findAsset['file_path']),
                'url'       => cmf_get_file_download_url($findAsset['file_path']),
                'path'      => $findAsset['file_path'],
                'name'      => $filename,
                'preview_url'=>cmf_get_image_preview_url($findAsset['file_path']),
                'filepath'=>$findAsset['file_path'],
            ];
            return json(['Status'=>'Ok','code' => 1, 'msg' => '', 'data' => $new_data]);
        } else {
            Log::record('callback: line '.__LINE__.' error: 验证签名失败', 'oss');
            header("http/1.1 403 Forbidden");
            exit();
        }
    }

}
